// Copyright(c) Sonosemi Medical Ltd. Embedded Software Team.
// Author: Zhang Yisong(zhangyisong@sonosemi.com)

#pragma once

#include "boost/mp11.hpp"
#include "internal/active_active.hpp"
#include "internal/active_thread_safe.hpp"
#include "internal/concepts/active_active.hpp"

namespace active {

template <concepts::SmConfigurable TConfig, concepts::Policable TPolicy,
          concepts::TaskConfigable TTaskConfig>
constexpr static auto config =
    internal::Active<TConfig, TPolicy, TTaskConfig>{};

template <typename T>
concept ItemConfigable = concepts::QueueItemable<T> or concepts::Policable<T>;
template <concepts::SmConfigurable TConfig, ItemConfigable... Ts>
constexpr auto Config(Ts...) {
  using ItemTuple = std::tuple<Ts...>;
  using namespace boost::mp11;
  using PolicyTuple = mp_filter<internal::is_policy, ItemTuple>;
  using SizeTuple = mp_filter<internal::is_queuesize, ItemTuple>;
  using PrioTuple = mp_filter<internal::is_priority, ItemTuple>;
  static_assert(std::tuple_size_v<SizeTuple> < 2);
  static_assert(std::tuple_size_v<PrioTuple> < 2);
  constexpr auto get_value = [](auto t) {
    if constexpr (mp_size<decltype(t)>::value > 0) {
      return mp_at_c<decltype(t), 0>::value;
    } else {
      return 0;
    }
  };
  constexpr auto kPrio = get_value(PrioTuple{});
  constexpr auto kSize = get_value(SizeTuple{});
  static_assert(
      not((kPrio != 0) and (kSize == 0)),
      "it is required to assign the queue size as the prio is assigned");
  using TaskConfig = internal::TaskConfig<kSize, kPrio>;
  using Policies =
      std::conditional_t<(mp_size<PolicyTuple>::value > 0),
                         mp_apply<mp_append, PolicyTuple>, internal::Policy<>>;

  return config<TConfig, Policies, TaskConfig>;
}

template <concepts::ActiveConfiguable TConfig, ItemConfigable... TItems>
consteval auto Config(TConfig, TItems...) {
  using namespace boost::mp11;
  using ActiveType = TConfig;
  using SmConfigType = ActiveType::SmConfigType;
  using ItemTuple = std::tuple<TItems...>;
  using PolicyTuple = mp_filter<internal::is_policy, ItemTuple>;
  using SizeTuple = mp_filter<internal::is_queuesize, ItemTuple>;
  using PrioTuple = mp_filter<internal::is_priority, ItemTuple>;

  using NewPolicies = mp_apply<mp_append, PolicyTuple>;
  using AppenedPolicies =
      mp_append<typename ActiveType::PolicyType, NewPolicies>;

  constexpr auto kOldSize = ActiveType::TaskConfigType::kQueueSize;
  constexpr auto kOldPrio = ActiveType::TaskConfigType::kPriority;

  static_assert(std::tuple_size_v<SizeTuple> < 2);
  static_assert(std::tuple_size_v<PrioTuple> < 2);

  constexpr auto get_value = [](auto t, auto old) {
    if constexpr (mp_size<decltype(t)>::value > 0) {
      return mp_at_c<decltype(t), 0>::value;
    } else {
      return old;
    }
  };
  constexpr auto kPrio = get_value(PrioTuple{}, kOldPrio);
  constexpr auto kSize = get_value(SizeTuple{}, kOldSize);

  static_assert(not(kPrio > 0 and kSize == 0));

  return config<SmConfigType, AppenedPolicies,
                internal::TaskConfig<kSize, kPrio>>;
}

template <typename TModule, ItemConfigable... TItems>
  requires(internal::is_active_config<decltype(TModule::config)>::value)
consteval auto Config(TItems... items) {
  return Config(TModule::config, items...);
}


} // namespace active